package com.pig4cloud.pig.model.aspect;


import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSON;
import com.google.gson.Gson;
import com.pig4cloud.pig.common.security.service.PigUser;
import com.pig4cloud.pig.common.security.util.SecurityUtils;
import com.pig4cloud.pig.model.api.dto.ManagementDto;
import com.pig4cloud.pig.model.api.dto.OptLogDTO;
import com.pig4cloud.pig.model.event.ModelLogEvent;
import com.pig4cloud.pig.model.holder.RequestHolder;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.utils.Lists;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEvent;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import javax.servlet.http.HttpServletRequest;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Method;
import java.util.*;

/**
 * @author springboot葵花宝典
 * @description: TODO
 */
@Slf4j
@Aspect
@Component
public class LogAspect {

    @Autowired
    private final ApplicationContext applicationContext;


    public LogAspect(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }

    @Pointcut("execution(* *..*controller.Proxy1Controller.*(..))")
    public void pointcut() {
    }

    /**
     * 环绕通知，使用Pointcut()上注册的切入点
     * @param point
     * @return
     */
    @Around("pointcut()")
    public Object recordLog(ProceedingJoinPoint point) throws Throwable {
        Object result = new Object();
        //　获取request
        HttpServletRequest request = RequestHolder.getHttpServletRequest();
        // 判断为空则直接跳过执行
        if (ObjectUtils.isEmpty(request)){
            return point.proceed();
        }
        //　获取注解里的value值
        Method targetMethod = resolveMethod(point);
        // 打印执行时间
        Date now = DateUtil.date();
        // 请求方法
        String method = request.getMethod();
        String url = request.getRequestURI();
        // 获取IP和地区
        String ip = RequestHolder.getHttpServletRequestIpAddress();
        //获取请求参数
        // 参数
        Object[] args = point.getArgs();
		ManagementDto managementDto= getArgs(args, request);
		managementDto.setUrl(url);
		String protocol = request.getProtocol();
		// 获取所有请求头
		Map<String,String> map=new HashMap<>();
		Enumeration<String> headerNames = request.getHeaderNames();
		while (headerNames.hasMoreElements()) {
			String headerName = headerNames.nextElement();
			String headerValue = request.getHeader(headerName);
			map.put(headerName,headerValue);
		}
		Date end = null;
        // 计算耗时
        long tookTime = 0L;
        try {
            result = point.proceed();
        } finally {
            end = DateUtil.date();

            tookTime = DateUtil.between(now, end, DateUnit.MS);
        }
		PigUser pigUser=SecurityUtils.getUser();
		String userName="admin";
        if(pigUser!=null){
			 userName = SecurityUtils.getUser().getName();
		}
        //　封装optLogDTO
        OptLogDTO optLogDTO = new OptLogDTO();
        optLogDTO.setIp(ip)
				.setPort("9999")
				.setProtocol(protocol)
                .setCreateBy(userName)
                .setMethod(method)
				.setOrginHeader(map.toString())
				.setModelId(map.get("model_id"))
                .setPath(url)
                .setStartTime(now)
                .setEndTime(end)
                .setType("1")
                .setOperation(String.valueOf(result))
                .setExecuteTime(tookTime)
                .setParams(managementDto);
        ApplicationEvent event = new ModelLogEvent(optLogDTO);
        //发布事件
        applicationContext.publishEvent(event);
        long id = Thread.currentThread().getId();
        System.out.println("发布事件,线程id：" + id);
        log.info("info Result: {}", JSON.toJSONString(optLogDTO));
        return result;
    }

    /**
     * 配置异常通知
     *
     * @param point join point for advice
     * @param e exception
     */
    @AfterThrowing(pointcut = "pointcut()", throwing = "e")
    public void logAfterThrowing(JoinPoint point, Throwable e) {
        // 打印执行时间
        long startTime = System.nanoTime();
        Date now = DateUtil.date();
        OptLogDTO optLogDTO = new OptLogDTO();
        // 获取IP和地区
        String ip = RequestHolder.getHttpServletRequestIpAddress();
        //　获取request
        HttpServletRequest request = RequestHolder.getHttpServletRequest();
        // 请求方法
        String method = request.getMethod();
        String url = request.getRequestURI();
		String protocol = request.getProtocol();
		Object[] args = point.getArgs();
		ManagementDto managementDto= getArgs(args, request);
		managementDto.setUrl(url);
		// 获取所有请求头
		Map<String,String> map=new HashMap<>();
		Enumeration<String> headerNames = request.getHeaderNames();
		while (headerNames.hasMoreElements()) {
			String headerName = headerNames.nextElement();
			String headerValue = request.getHeader(headerName);
			map.put(headerName,headerValue);
		}
		PigUser pigUser=SecurityUtils.getUser();
		String userName="admin";
		if(pigUser!=null){
			userName = SecurityUtils.getUser().getName();
		}
        //　获取注解里的value值
        Method targetMethod = resolveMethod((ProceedingJoinPoint) point);
        optLogDTO.setExecuteTime( DateUtil.between(now, DateUtil.date(), DateUnit.SECOND))
                .setIp(ip)
				.setPort("9999")
				.setProtocol(protocol)
                .setMethod(method)
				.setOrginHeader(map.toString())
				.setModelId(map.get("model_id"))
				.setPath(url)
				.setCreateBy(userName)
                .setType("2")
				.setParams(managementDto)
                .setException(getStackTrace(e));
        // 发布事件
        log.error("Error Result: {}",  JSON.toJSONString(optLogDTO));
        ApplicationEvent event = new ModelLogEvent(optLogDTO);
        //发布事件
        applicationContext.publishEvent(event);
        long id = Thread.currentThread().getId();
        System.out.println("发布事件,线程id：" + id);
    }

    private Method resolveMethod(ProceedingJoinPoint point) {
        MethodSignature signature = (MethodSignature) point.getSignature();
        Class<?> targetClass = point.getTarget().getClass();
        Method method = getDeclaredMethod(targetClass, signature.getName(),
                signature.getMethod().getParameterTypes());
        if (method == null) {
            throw new IllegalStateException("无法解析目标方法: " + signature.getMethod().getName());
        }
        return method;
    }

    /**
     * 获取堆栈信息
     */
    public static String getStackTrace(Throwable throwable) {
        StringWriter sw = new StringWriter();
        try (PrintWriter pw = new PrintWriter(sw)) {
            throwable.printStackTrace(pw);
            return sw.toString();
        }
    }

    private Method getDeclaredMethod(Class<?> clazz, String name, Class<?>... parameterTypes) {
        try {
            return clazz.getDeclaredMethod(name, parameterTypes);
        } catch (NoSuchMethodException e) {
            Class<?> superClass = clazz.getSuperclass();
            if (superClass != null) {
                return getDeclaredMethod(superClass, name, parameterTypes);
            }
        }
        return null;
    }

    /**
     * 获取请求参数
     * @param args
     * @param request
     * @return
     */
    private ManagementDto getArgs(Object[] args, HttpServletRequest request) {
		ManagementDto managementDto = new ManagementDto();
//        try {
//            if (!request.getContentType().contains("multipart/form-data")) {
//				managementDto=(ManagementDto)args[0];
//            }
//        } catch (Exception e) {
//            try {
//				managementDto=(ManagementDto)args[0];
//            } catch (Exception ex) {
//                log.warn("解析参数异常", ex);
//            }
//        }
		String method = request.getMethod();
		managementDto.setMethod(method);
		try{
			if (method.equalsIgnoreCase(HttpMethod.GET.name())) {
				managementDto.setParameterType("0");
				List<Object> list= Lists.newArrayList();
				Map<String, String[]> parameterMap = request.getParameterMap();
				parameterMap.forEach((key, value) -> {
                    Map<String,String> map=new HashMap<>();
					map.put("name",key);
					map.put("defaultValue",value[0]);
					list.add(map);
				});
				managementDto.setParameter(list.toString());
				System.out.println(managementDto);
			} else if (method.equalsIgnoreCase(HttpMethod.POST.name())) {
				managementDto.setParameterType("1");
				String requestBody="";
				if (!request.getContentType().contains("multipart/form-data")) {
					requestBody=new Gson().toJson( args[0]);
				}
				System.out.println(requestBody);
				managementDto.setParameter(requestBody);
			}
		}catch (Exception e){
			log.warn("解析参数异常", e);
		}

        return managementDto;
    }
}
